package com.hhsj.design.pullableview.testScroller;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;

public class RefreshScrollViewX extends ScrollView {

    /**
     * 1.如果listview滑出屏幕的距离为0时，上拉时不拦截处理，下拉时拦截处理，header下面的view慢慢显示出来
     * 2.如果listview滑出屏幕的距离不为0时，执行其自身的滑动
     * 3.如果正在向下显示header下面的view时，上滑view也向上隐藏，隐藏完继续listview自身的滑动
     */
    private LinearLayout mHeaderParent;
    private LinearLayout mHeaderLayout;
    private FrameLayout mRefreshLayout;
    private View mRefreshView;
    private LinearLayout mContainer;
    private int mTouchSlop;
    private int mRefreshViewHeight;
    private int mHeaderHeight;
    private int mTargetHeight;
    private int mMaxHeight;
    private float mLastX, mLastY;
    private boolean mIsRefresh, mIsDrag;
    private ViewGroup.LayoutParams params;
    private BackRunnable mRunnable;
    private BackRefreshStateRunnable mBackRefreshStateRunnable;
    private RefreshListener mListener;
    private boolean supportRefresh = false;

    public interface RefreshListener {
        void start();
    }

    public RefreshScrollViewX(Context context) {
        this(context, null);
    }

    public RefreshScrollViewX(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RefreshScrollViewX(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        int count = getChildCount();
        if (count != 1 || !(getChildAt(0) instanceof LinearLayout)) {
            throw new IllegalArgumentException("scrollview must be one LinearLayout");
        }
        mContainer = (LinearLayout) getChildAt(0);
        initHeader();
    }

    public void setRefreshListener(RefreshListener listener) {
        mListener = listener;
    }


    private void initHeader() {
        //头部布局父布局，高度自适应
        mHeaderParent = new LinearLayout(getContext());
        mHeaderParent.setOrientation(LinearLayout.VERTICAL);
        mHeaderParent.setGravity(Gravity.CENTER_HORIZONTAL);
        mHeaderParent.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        mContainer.addView(mHeaderParent, 0);


        //header 布局父布局，线性竖直排列，高度自适应
        mHeaderLayout = new LinearLayout(getContext());
        mHeaderLayout.setOrientation(LinearLayout.VERTICAL);
        mHeaderParent.addView(mHeaderLayout, 0);

        //刷新布局父布局，高度由下拉距离决定
        mRefreshLayout = new FrameLayout(getContext());
        mRefreshLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 0));
        mHeaderParent.addView(mRefreshLayout);

    }

    public void addHeaderView(View header) {
        if (header != null) {
            mHeaderLayout.addView(header);
        }
    }

    public void addRefreshView(View refreshHeader) {
        if (mRefreshLayout.getChildCount() > 0) {
            mRefreshLayout.removeAllViews();
        }

        mRefreshView = refreshHeader == null ? createDefalutRefreshView() : refreshHeader;
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, 200);
        lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
        mRefreshView.setLayoutParams(lp);
        mRefreshLayout.addView(mRefreshView);
        mHeaderParent.requestLayout();
    }


    private View createDefalutRefreshView() {
        LinearLayout linearLayout = new LinearLayout(getContext());
        linearLayout.setMinimumHeight(200);
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        linearLayout.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
        linearLayout.setPadding(0, 30, 0, 10);
        linearLayout.addView(new ProgressBar(getContext()));
        return linearLayout;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (mRefreshView == null || mHeaderLayout == null || getScrollY() > 0) {
            return super.onInterceptTouchEvent(ev);
        }
        int action = ev.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = ev.getX();
                mLastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (isValidDrag(ev)) {
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }


    public void setRefreshEnable(boolean support) {
        supportRefresh = support;
    }



    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        //需要处理header view里面的显示的话，要设置param，否则给父类自己来处理
        if (!supportRefresh || mRefreshView == null || mHeaderLayout == null || getScrollY() > 0) {
            return super.onTouchEvent(ev);
        }

        if (mRefreshViewHeight <= 0) {
            mRefreshViewHeight = mRefreshView.getMeasuredHeight();
            mTargetHeight = mRefreshViewHeight;
        }

        Log.e("HHSJHHSJ", "mRefreshViewHeight = " + mRefreshViewHeight);

        int action = ev.getActionMasked();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mLastX = ev.getX();
                mLastY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (getScrollY() > 0 || mIsRefresh) {
                    break;
                }
                if (!mIsDrag && isValidDrag(ev)) {
                    mIsDrag = true;
                }

                if (mIsDrag) {
                    if (ev.getY() - mLastY > 0 && !mIsRefresh) {
                        moveLayout(ev.getY() - mLastY);
                        return true;
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
                mIsDrag = false;
                if (params == null) {
                    params = mRefreshLayout.getLayoutParams();
                }

                if (params.height <= 0) {
                    break;
                }

                mIsRefresh = params.height >= mTargetHeight;

                if (!mIsRefresh) {
                    //回弹到刷新的高度
                    completeRefresh();
                } else {

                    startRefreshHeight(params.height, mTargetHeight);

                    startRefreshAnimator();

                    if (mListener != null) {
                        mListener.start();
                    }
                }
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 执行刷新动画
     */
    protected void startRefreshAnimator() {

    }

    /**
     * 弹到开始刷新的高度
     */
    private void startRefreshHeight(int height, int targetHeight) {
        if (mBackRefreshStateRunnable == null) {
            mBackRefreshStateRunnable = new BackRefreshStateRunnable(500, height, targetHeight);
        }
        mBackRefreshStateRunnable.start();
    }


    /**
     * 当下拉的时候，需要拦截
     */
    private boolean isValidDrag(MotionEvent ev) {
        if (ev == null) {
            return false;
        }
        return Math.abs(mLastX - ev.getX()) < Math.abs(mLastY - ev.getY())//下拉
                && Math.abs(mLastY - ev.getY()) > mTouchSlop;//
    }


    public void completeRefresh() {
        if (mRunnable == null) {
            mRunnable = new BackRunnable(500);
        }
        mRunnable.start();
    }

    private void moveLayout(float y) {
        params = mRefreshLayout.getLayoutParams();
        float changeHeight;
        if (y < mTargetHeight) {
            changeHeight = y;
        } else {
            changeHeight = mTargetHeight + (float) (Math.pow(y - mTargetHeight,0.8));
        }
        params.height = (int) changeHeight;
        mRefreshLayout.requestLayout();
    }


    private class BackRunnable implements Runnable {

        private int mDuration;
        private boolean mIsFinish;
        private long mStartTime;

        private Interpolator mInterpolator = new Interpolator() {

            @Override
            public float getInterpolation(float input) {
                return (float) Math.pow(input, 5);
            }
        };

        public BackRunnable(int duration) {
            mDuration = duration;
            mIsFinish = true;
        }

        public void start() {
            if (mIsFinish) {
                mStartTime = System.currentTimeMillis();
                mIsFinish = false;
                post(this);
            }
        }

        public void cancel() {
            this.mIsFinish = true;
        }

        @Override
        public void run() {
            if (!mIsFinish) {
                long delta = System.currentTimeMillis() - mStartTime;
                if (delta > mDuration) {
                    delta = mDuration;
                }
                params.height = (int) (mHeaderHeight +
                        ((mRefreshView.getY() + mRefreshViewHeight) - mHeaderHeight) * mInterpolator.getInterpolation(1f - delta * (1f / mDuration)));
                Log.e("HHSJHHSJ", "params.height = " + params.height);

                mHeaderLayout.requestLayout();
                if (params.height == mHeaderHeight) {
                    this.mIsFinish = true;
                    mIsRefresh = false;
                    return;
                }
                post(this);
            }
        }
    }

    private class BackRefreshStateRunnable implements Runnable {

        private int mDuration;
        private boolean mIsFinish;
        private long mStartTime;
        private int height;
        private int targetHeight;


        private Interpolator mInterpolator = input -> (float) Math.pow(input, 5);

        public BackRefreshStateRunnable(int duration, int height, int targetHeight) {
            mDuration = duration;
            mIsFinish = true;
            this.height = height;
            this.targetHeight = targetHeight;

        }

        public void start() {
            if (mIsFinish) {
                mStartTime = System.currentTimeMillis();
                mIsFinish = false;
                post(this);
            }
        }

        public void cancel() {
            this.mIsFinish = true;
        }

        @Override
        public void run() {
            if (!mIsFinish) {
                long delta = System.currentTimeMillis() - mStartTime;
                if (delta > mDuration) {
                    delta = mDuration;
                }
                params.height = (int) (targetHeight +
                        ((mRefreshView.getY() + mRefreshViewHeight) - targetHeight) * mInterpolator.getInterpolation(1f - delta * (1f / mDuration)));
                mHeaderLayout.requestLayout();
                if (params.height == targetHeight) {
                    this.mIsFinish = true;
                    mIsRefresh = false;
                    return;
                }
                post(this);
            }
        }
    }

}